home *** CD-ROM | disk | FTP | other *** search
- /**************************************************************************
- *
- * File: band.c
- *
- * Description: Prints detailed information about a STIFF format file.
- * The header and IFD information is printed. For each IFD the tags
- * are printed. Offsets for IFD's and data are also displayed.
- * If a file is not specified on the command-line, stdin is read.
- * Converts the file to Banded data. Currently, banded data is not
- * supported by STIFF.
- *
- * band.c takes chunky/planar data and converts it to banded data.
- *
- * With the following inputs,
- * RGB ( 3 color ) 1 bit per sample, chunky ( with STIFF header )
- *
- * RGB0RGB0RGB0 .... _____ End of Line 0
- * RGB0RGB0RGB0 .... _____ End of Line 1
- * ...
- *
- * the output will be in the following format:
- * RGB ( 3 color ) 1 bit per sample, banded ( without STIFF header )
- *
- * RRRRR ....
- * GGGGG ....
- * BBBBB .... _____ End of Line 0
- * RRRRR ....
- * GGGGG ....
- * BBBBB .... _____ End of Line 1
- * ...
- *
- * i.e, the data specific to a color in each line is contiguous.
- *
- * Usage: band [STIFF file] [outfile]
- *
- **************************************************************************/
-
-
- #ident "$Revision: 1.1 $"
-
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
- #include <stiff.h>
- #include <printstiff.h>
-
- #define ST_PLANE_BANDED 3
-
- #define MASK4 ((char)0x0f)
-
- #define WR_FLAGS ( O_CREAT | O_TRUNC | O_WRONLY )
-
- #define ROUND(wd, n) ( (wd) % (n) ? (wd)/(n) + 1 : (wd)/(n) )
- #define RND_BYTE(wd) ( ROUND(wd, 8) )
-
- #define MESSAGE stderr
-
- #define UMASK (0644)
-
- static int output;
-
- static char *progname = NULL;
-
- static char *linebuf = NULL;
- static int linelen = 0;
-
- int ConvertImage(char *buf, PSTImageHeader *ifd);
- int PackedToBand(char *buf, PSTImageHeader *ifd);
- int PlanarToBand(char *buf, PSTImageHeader *ifd);
- int DumpBuffer( int out, void *buf, int size);
-
- int ChunkyOneToBand( char *buf, int width, int height, int spp );
- int ChunkyFourToBand( char *buf, int width, int height, int spp );
- int ChunkyEightToBand( char *buf, int width, int height, int spp );
-
- #define DUMP(num) fprintf(MESSAGE, "%.2x ", (num))
-
- int DumpBuffer( int out, void *buf, int size)
- {
- if ( out < 1 || !buf || size <= 0 )
- return 0;
- return write( out, buf, size);
- }
-
- main(int argc, char *argv[])
- {
- STStream *stptr; /* Stream TIFF pointer to read */
- PSTImageHeader ifd; /* Struct to get image headers */
- int n = 0; /* Number of images read so far */
- unsigned long ifdOffset; /* Maintains offset of next IFD */
- int input; /* input file descriptor */
- char *imgbuf = NULL; /* Image Buffer */
- int imglen = 0; /* Image Buffer length */
- int nbytes = 0;
- int first=1;
-
- if ((progname = strrchr(argv[0],'/')) != NULL) progname++;
- else progname = argv[0];
-
- /*
- * input is from file if a file has been specified on the command
- * line, or standard input if no files were specified.
- */
- input = argc > 1 ? open(argv[1], O_RDONLY) : fileno(stdin);
- output = argc > 2 ? open(argv[2], WR_FLAGS, UMASK ) : fileno(stdout);
-
- if (input < 0 || output < 1) {
- perror(progname);
- exit(1);
- }
-
- /*
- * Open a TIFF stream
- */
- if ((stptr = STOpen(input, ST_READ)) == NULL) {
- STPerror(progname);
- exit(1);
- }
-
- /*
- * Print file header information
- */
- (void)fprintf(MESSAGE, "TIFF Header (offset: 0)\n");
- (void)fprintf(MESSAGE, "First IFD at offset: 0x%04lX\n", stptr->next);
- (void)fprintf(MESSAGE, "\n");
-
- /*
- * Read and display IFD information until PSTReadImageHeader
- * returns -1 indicating that no more IFDs can be read from the
- * stream.
- */
- ifdOffset = stptr->next;
- while (PSTReadImageHeader(stptr, &ifd) >= 0) {
- /*
- * Print the offset of this IFD, which we got out of stptr
- * before calling STReadImageHeader
- */
- n++;
- (void)fprintf(MESSAGE, "IFD %d (offset: 0x%04lX)\n", n, ifdOffset);
- /*
- * Now update ifdOffset for next time through the loop.
- */
- ifdOffset = stptr->next;
-
- /*
- * Print the tags
- */
-
- if (STPrintTags(MESSAGE, stptr) < 0) {
- STPerror(progname);
- exit(1);
- }
-
- /*
- * Print the STIFF Image Header structure
- */
- (void)fprintf(MESSAGE, "STIFF Image Header\n");
- (void)fprintf(MESSAGE, "\tImage width: %lu\n", ifd.width);
- (void)fprintf(MESSAGE, "\tImage height: %lu\n", ifd.height);
- (void)fprintf(MESSAGE, "\tBits per sample: %u\n", ifd.bitsPerSample);
- (void)fprintf(MESSAGE, "\tSamples per pixel: %u\n", ifd.samplesPerPixel);
- (void)fprintf(MESSAGE, "\tImage size (bytes): %lu\n", ifd.imgbytes);
- (void)fprintf(MESSAGE, "\tImage type: %u\n", ifd.type);
- (void)fprintf(MESSAGE, "\tData format: %s\n",
- (ifd.plane == ST_PLANE_PACKED ? "chunky / packed" :
- (ifd.plane == ST_PLANE_PLANAR ? "planar" : "banded")) );
-
- /*
- * Create the buffer for storing the image.
- */
- if ( first ) {
- imglen = ifd.imgbytes;
- imgbuf = (char *)calloc(1, imglen + 1);
- first = 0;
- } else if ( imglen < ifd.imgbytes ) {
- imglen = ifd.imgbytes;
- imgbuf = (char *)realloc(imgbuf, imglen + 1);
- }
-
- /*
- * Convert the input Planar/Chunky image to Band format.
- * And print the output to stdout.
- */
- if ( (nbytes = STRead(stptr, imgbuf, ifd.imgbytes)) < 0 ||
- nbytes < ifd.imgbytes ) {
- fprintf(MESSAGE, " Could not read image data \n");
- break;
- } else {
- ConvertImage(imgbuf, &ifd);
- }
- }
-
- if ( imgbuf )
- free(imgbuf);
- if ( linebuf )
- free(linebuf);
- /*
- * STReadImageHeader will return -1 with STerrno set to STEEOF to
- * indicate that there are no more images. We don't consider this
- * an error unless no images at all were read in.
- */
- if (STerrno != STEEOF || n == 0) {
- STPerror(progname);
- }
-
- /*
- * Close the TIFF stream
- */
- STClose(stptr);
-
- return 0;
- }
-
-
- /*
- * RETURNS: -1 EOF, 0 Error, # no. of bytes written.
- */
- int ConvertImage(char *buf, PSTImageHeader *ifd)
- {
- int totalbytes = 0, nbytes = 0;
-
- switch ( ifd->type ) {
- case ST_TYPE_K:
- case ST_TYPE_W:
-
- /*
- ifd->plane = ST_PLANE_BANDED;
- nbytes = DumpBuffer(output, ifd, sizeof(PSTImageHeader));
- if ( nbytes <= 0 ) return nbytes;
- totalbytes += nbytes;
- */
- nbytes = DumpBuffer(output, buf, ifd->imgbytes);
- if ( nbytes <= 0 ) return nbytes;
- totalbytes += nbytes;
-
- return totalbytes;
- }
-
- if ( ifd->plane == ST_PLANE_PACKED ) { /* chunky format */
- totalbytes = PackedToBand(buf, ifd);
- } else if ( ifd->plane == ST_PLANE_PLANAR ) { /* planar format */
- totalbytes = PlanarToBand(buf, ifd);
- } else {
- fprintf(MESSAGE, " Unknown data \n");
- return 0;
- }
-
- return totalbytes;
- }
-
- /*
- * Convert from chunky format to banded format.
- */
- int PackedToBand(char *buf, PSTImageHeader *ifd)
- {
-
- int spp = ifd->samplesPerPixel;
- int bps = ifd->bitsPerSample;
- int width = ifd->width;
- int height = ifd->height;
- int totalbytes = 0;
- int (*ConvertToBand)(char *, int, int, int);
-
- /*
- * In chunky format, for 3 color 1 bitPerSample, a bit is padded to
- * fit into a nibble. So samplesPerPixel shows as 4 though it is
- * actually 3. For more than 1 bitPerSample ( 4 & 8 ), no padding is done
- * as long as the data does not end in the middle of a byte.
- *
- * Here, we find out the actual number of samples (colors) per pixel.
- */
- switch ( ifd->type ) {
- case ST_TYPE_RGB:
- case ST_TYPE_YMC:
- case ST_TYPE_CMY:
- spp = 3;
- break;
- case ST_TYPE_YMCK:
- case ST_TYPE_KCMY:
- case ST_TYPE_CMYK:
- spp = 4;
- break;
- default:
- spp = 3;
- break;
- }
-
- width = ROUND(ifd->imgbytes, height);
-
- /*
- * The following computation is only to print the actual banded width.
- * But we pass the width of chunky/planar directly to the sub functions.
- */
- width = spp * ROUND(width, (bps == 1 ? 4 : spp));
- fprintf(MESSAGE, "Banded image width in bytes = %d , %d colors\n",
- width, spp );
- fprintf(MESSAGE, "Banded image size in bytes = %d \n", width * height );
-
- width = ROUND(ifd->imgbytes, height);
-
- /*
- * chunky/Packed format has all the 3/4 colors in a nibble ( 4 bits ).
- *
- * We just pick-up the various colors in a nibble and
- * put them in banded format. So for example, if the input
- * is in YCMK format ( Samples Per Pixel = 4 ), we extract
- * all the Y's and put them contiguously so also C, M and K data.
- * So in banded format, first we have Y line, then C, then M and
- * then K line in that order.
- * For a 1 bitPerSample, 3 samplesPerPixel type format we have only
- * 3 lines corresponding to each line of the input chunky format.
- * So in fact the output data will be lesser than chunky format data,
- * because, we are stripping the padded bit.
- */
- switch ( bps ) {
- default:
- case 1:
- ConvertToBand = ChunkyOneToBand;
- break;
- case 4:
- ConvertToBand = ChunkyFourToBand;
- break;
- case 8:
- ConvertToBand = ChunkyEightToBand;
- break;
- }
- totalbytes = ConvertToBand( buf,
- width,
- height,
- spp );
-
- return totalbytes;
- }
-
- int ChunkyOneToBand( char *buf, int width, int height, int ncolors )
- {
- int mask=0, offset=0, color[4];
- int count=0, nbytes=0, totalbytes=0;
- int i, j, k;
-
- /*
- * width represents the width of all the colors put together.
- */
- offset = ROUND(width, 4);
-
- if ( !linebuf ) {
- linelen = ncolors * offset;
- linebuf = (char *)calloc(1, linelen);
- } else if ( linelen < ncolors * offset ) {
- linelen = ncolors * offset;
- linebuf = (char *)realloc(linebuf, linelen);
- }
-
- /*
- * In the case of 1 bit per sample, the input byte contains 2 samples.
- * But when the no. of colors used is 3, padding is done to fill
- * the nibble. So we mask out the padded bit when 'ncolors' is 3.
- */
- mask = ncolors == 3 ? 0x02 : 0x01;
-
- color[0] = color[1] = color[2] = color[3] = 0;
-
- while ( height-- ) {
- for ( count = i = 0; i < width; count++, i += 4 ) {
- /*
- * Extract the bits from 4 bytes of the input buffer.
- * This makes a full byte because, each input buffer byte
- * contains 2 bits of a particular color.
- */
- for ( j = 0; j < 4; j++ ) {
- register int byte = buf[i+j];
-
- /*
- * Extract the bits for each color.
- * ex: for CMY, k=0 ==> Y & k=2 ==> C.
- * and color[0] is C ... color[2] is Y.
- */
- for ( k=0; k < ncolors; k++ ) {
- register int b = color[ncolors - k - 1];
-
- /* first time we expect 'b' to be 0 */
- b <<= 1;
- b |= ( (byte & ((mask << k) << 4 )) ?
- 0x01 : 0x00 );
- b <<= 1;
- b |= ( (byte & (mask << k)) ?
- 0x01 : 0x00 );
- color[ncolors - k - 1] = b;
- }
- }
-
- /*
- * Bytes for all the colors have been formed.
- * So copy them into 'linebuf' & init them back to 0.
- * The first color starts at location 0, 2nd at '1*offset'...
- */
- for ( k=0; k < ncolors; color[k++] = 0 )
- linebuf[count+k*offset] = color[k];
- }
-
- if ( (nbytes = DumpBuffer(output, linebuf, ncolors * offset)) <= 0 )
- return nbytes;
- totalbytes += nbytes;
- buf += width;
- }
-
- return totalbytes;
-
- }
-
- int ChunkyFourToBand( char *buf, int width, int height, int ncolors )
- {
- int nbytes=0, totalbytes=0;
- int offset = 0;
- int nbits=4, nsamples=0;
- int index = 0, count = 0;
- int i, j, k;
- register char mask = MASK4;
- register char byte=0;
- register char nib=0;
-
- nsamples = (width * (8/nbits))/ncolors;
- offset = ROUND( nbits*nsamples, 8 );
-
- if ( !linebuf ) {
- linelen = ncolors * offset;
- linebuf = (char *)calloc(1, linelen);
- } else if ( linelen < ncolors * offset ) {
- linelen = ncolors * offset;
- linebuf = (char *)realloc(linebuf, linelen);
- }
-
- while ( height-- ) {
- for ( j = 0; j < ncolors; j++ ) {
-
- index = count = byte = 0;
- mask = j%2 ? MASK4 : ~MASK4;
-
- for ( i = 0; i < width && count < nsamples ; byte=0) {
- for ( k = 0; count < nsamples && k < 2; nib=0, k++ ) {
- nib = buf[i] & mask;
- nib = mask & MASK4 ? nib : MASK4 & (nib >> nbits);
- byte |= nib;
- if ( !k )
- byte <<= nbits;
- i = nbits * ( j + ++count * ncolors )/8;
- mask = ~mask;
- }
- linebuf[index++ + offset * j] = byte;
- }
- }
- if ( (nbytes = DumpBuffer(output, linebuf, ncolors * offset)) <= 0 )
- return nbytes;
- totalbytes += nbytes;
- buf += width;
- }
-
- return totalbytes;
- }
-
- int ChunkyEightToBand( char *buf, int width, int height, int ncolors )
- {
- int nbytes=0, totalbytes=0;
- int offset = 0, nsamples=0;
- int j, count=0;
-
- offset = nsamples = ROUND(width, ncolors);
-
- if ( !linebuf ) {
- linelen = ncolors * offset;
- linebuf = (char *)calloc(1, linelen);
- } else if ( linelen < ncolors * offset ) {
- linelen = ncolors * offset;
- linebuf = (char *)realloc(linebuf, linelen);
- }
-
- while ( height-- ) {
- for ( j = 0; j < ncolors; j++ ) {
- for ( count = 0; count < nsamples; count++ )
- linebuf[count + offset*j] = buf[j + count*ncolors];
- }
- if ( (nbytes = DumpBuffer(output, linebuf, ncolors * offset)) <= 0 )
- return nbytes;
- totalbytes += nbytes;
- buf += width;
- }
-
- return totalbytes;
- }
-
-
- /*
- * Convert from planar format to banded format.
- */
- int PlanarToBand(char *buf, PSTImageHeader *ifd)
- {
-
- int spp = ifd->samplesPerPixel;
- int width = ifd->width;
- int height = ifd->height;
- int nbytes=0, totalbytes=0;
- int i=0, j=0;
-
- /*
- * It's always better to resolve the spp.
- * So, here we find out the actual number of samples (colors) per pixel.
- */
- switch ( ifd->type ) {
- case ST_TYPE_RGB:
- case ST_TYPE_YMC:
- case ST_TYPE_CMY:
- spp = 3;
- break;
- case ST_TYPE_YMCK:
- case ST_TYPE_KCMY:
- case ST_TYPE_CMYK:
- spp = 4;
- break;
- default:
- spp = 3;
- break;
- }
-
- width = ROUND(ifd->imgbytes, height);
-
- fprintf(MESSAGE, "Banded image width in bytes = %d , %d colors\n",
- width, spp );
- fprintf(MESSAGE, "Banded image size in bytes = %d \n", width * height );
-
- width = ROUND(width, spp);
-
- for ( i=0; i < height; i++ ) {
- for ( j = 0; j < spp; j++ ) {
- nbytes = DumpBuffer(output, &buf[j*width*height + i], width);
- if ( nbytes <= 0 ) return nbytes;
- totalbytes += nbytes;
- }
- }
- return totalbytes;
- }
-
-